unit kgs_postings;

{*------------------------------------------------------------------------------




  @Author    yap
  @Version   2007.02.10   yap	Initial revision
  2007.2.10 fix .prev problem
  2007.3.22 port to linux
  2007.3.30 uncompress milestone,
            extracting milestone for LINES is time consuming on enwiki
  2007.10.12 prev has problem, use a slow version


-------------------------------------------------------------------------------}


// 64 bits postlist
interface
uses sysutils,classes,tempfile;

type
TMileStone = record
  value:int64;
  Count:int64;
end;

PMileStone=^TMileStone;

TVintList_wo = class;
//TOnFlushRequest = procedure (pl:TVintList_wo) of object;

TVintList_wo = class(TPersistent)
  protected
    fdata:pchar;
    fsize:cardinal;
    fFilled:cardinal;      // filled byte

    fcount:cardinal;     // count of added offset

    fflushcount:cardinal;   // flush count
    fmilestonedirty:boolean; // set when pop is called
    fflushptr:int64;
    fptr:pchar;


    function milestoneneeded:cardinal;
    procedure grow;
  public
    lastadded:int64;
    countonly:boolean;
    counting:int64;
//    onFlushRequest:TOnFlushRequest;
//    isFlushcandidate:boolean;
//    keyoffset:integer;

    constructor create;//(pf:TKNPackedIntFeatures=[pfstore,pfmilestone]);virtual;
    constructor createfromints(ints:PINTEGER;count:integer);
    function adddelta(const delta:int64):boolean;
    function add(const offset:int64):boolean;
    property count:cardinal read fcount;
    property filled:cardinal read ffilled;

    procedure clear; virtual;
    destructor destroy;override;
//    function writetostream(writeit:boolean=true):int64; override;
//    procedure fromstring(str:widestring);override;

    procedure freedata;
    procedure flush;
    property flushcount:cardinal read fflushcount;
    property flushptr:int64 read fflushptr;
    property size:cardinal read fsize;
end;

TVintList = class(TVintList_wo)
  private
    callednext:cardinal;
    fmilestones:pMilestone;
    fmssize:integer;
    nMileStone:integer;  // filled

    function FindValue(const value:int64; var Index: Integer): Boolean;
    function FindCount(const count:cardinal; var Index: Integer): Boolean;
    function MilestonebyCount(count:cardinal):PMileStone;
    function Milestonebyoffset(offset:int64):PMileStone;
    function MileStoneCount(ms:PMileStone):integer;
    procedure goMileStone(ms:PMileStone);
    function get_ms(i:integer):int64;
//    procedure readmilestone;
//    procedure readmilestone_map;
    procedure calentries;
    procedure fromflushed(const pl:TVintList_wo );overload;
    procedure unpackmilestone(ms_offset,ms_count:Tstream);

  public
    temporary:boolean;
    exclude:boolean;    // for NOT phrase
    atend:boolean;
    curvalue:int64;
    curcount:int64;
    wildcard:integer;
    fromdisk:boolean;

    constructor create;
    function next:int64;
    function prev:int64;
    function first:int64;
    procedure gobegin;
    function get(i:integer):int64;
    property items[i:integer]:int64 read get;default;
    property count:cardinal read fcount;
    procedure clear; override;
    procedure addmilestone(v,c:int64);
    procedure buildmilestone;
    procedure copyfrom(const v: TVintList_wo);
    destructor destroy;override;

   // getnext
   // find by milestone
//    function readfromstream(size:int64=0):int64;override;
//    function writetostream(writeit:boolean):int64;override;

   //justbigger
    function justbigger(offset:int64):int64;
    function justsmaller(offset:int64):int64;

    property nms:integer read nmilestone;
    property data:pchar read fdata;
    property size:cardinal read ffilled;
    function packmilestone(ms_offset,ms_count:Tstream):boolean;

    procedure readfromstream(inverted,ms_offset,ms_count:TStream);

end;
function writevint(s:Tstream;v:int64;writeit:boolean=true):integer;
function readvint(s:Tstream;adv:pinteger=nil):int64;

implementation
const
   sizethreshold = 32768;//8192;  // add to candidate list if postings sz exceed

var
  FirstMileStone: TMileStone;

const
//  maxsize = 512;
  INITIALSIZE=8;
  MASK_LEADBYTE = $0;
  MASK_FOLLOWBYTE = $80;
  defaultmilestone = 8;

  MileDistanceMask = $1FF;
  MileDistance = 512;

  MAXINCREMENT = 1024*1024;

function getincrement(sz:integer):integer;
begin
  result:=sz div 4;
  if result<8 then result:=8;
  if result>MAXINCREMENT then result:=MAXINCREMENT;
end;

function writevint(s:Tstream;v:int64;writeit:boolean=true):integer;
var
  delta:int64;
  n:integer;
  vint:array [0..10] of char; //enought to hold int64 and termintor
begin
  delta:=v;
  vint[0]:=char(MASK_LEADBYTE or (delta and $7F));
  delta := delta shr 7;
  n:=1;
  while delta>0 do begin
    vint[n]:=char(MASK_FOLLOWBYTE or (delta and $7F));
    inc(n);
    delta := delta shr 7;
  end;
  vint[n]:=#0;//terminator
  if writeit then result:=s.write(vint,n) else result:=n;
end;

function readvint_memory(vint:pchar;var adv:integer):int64;
var
  i,shift:integer;
begin
  shift:=0;
  result:=0;
  i:=0;
  repeat
    inc(result, (integer(vint[i]) and $7f) shl shift);
    inc(shift,7);
    inc(i);
  until (integer(vint[i]) and MASK_FOLLOWBYTE=0) ;

  adv:=i;
end;

function readvint(s:Tstream;adv:pinteger=nil):int64;
var
  a:integer;
  r,oldpos:int64;
  vint:array [0..10] of char; //enought to hold int64 and termintor
begin
  oldpos:=s.position;
  r:=s.read(vint,high(vint)-1);
  vint[r]:=#0;
  result:=readvint_memory(vint,a);
  s.position:=oldpos+a;
  if adv<>nil then adv^:=a;
end;


{TVintList_wo}
constructor TVintList_wo.create;
begin
  fsize:=INITIALSIZE;
  getmem(fdata,fsize);

  lastadded:=0;
  fflushptr:=FLUSH_TERMINATOR;
  fptr:=fdata;
end;

procedure TVintList_wo.freedata;
begin
  if assigned(fdata) then freemem(fdata);
end;

destructor TVintList_wo.destroy;
begin
  freedata;
end;
function TVintList_wo.milestoneneeded:cardinal;
begin
  result:=(fsize div miledistance)+1;
end;
procedure TVintList_wo.clear;
begin
  ffilled:=0;
  fcount:=0;
  lastadded:=0;

  if fsize>INITIALSIZE*2 then begin
    freedata;
    fsize:=INITIALSIZE;
    getmem(fdata,fsize);
  end;
end;

procedure TVintList_wo.grow;
var
  fptr_offset:cardinal;
begin

  if (ffilled>sizethreshold) then begin
    flush;   // filled set to 0 after flush
    exit;
  end;

  try
    fptr_offset:= integer(fptr)- integer(fdata);
  except
     raise exception.create('error fptr');
  end;

  inc(fsize,getincrement(fsize));
  reallocmem(fdata,fsize);
  fptr:=fdata;
  inc(fptr,fptr_offset);

end;

procedure TVintList_wo.flush;
var
  data:pchar;size:cardinal;
begin
  inc(fflushcount);
  data := fdata;
  size := ffilled;

  tempfile.slflushed.add( format('temp.position:%d pl:%d pl.filled:%d',
      [_temp.position,integer(self),filled]));

  fflushptr:=writetempblock(data,size,fflushptr);

//  freemem(fdata);
//  fsize:=INITIALSIZE * 64;
//  getmem(fdata,fsize);
  //keep memory after flush

  fptr:=fdata;
  ffilled:=0;
end;

function TVintList_wo.add(const offset:int64):boolean;
var
  delta:int64;
begin
  delta:=offset-lastAdded;
  if delta<0 then begin
    // raise Exception.create('PostList.add Delta<0');
     delta:=-1;
     result:=false;
     exit;
  end;
  if delta>-1 then adddelta(delta);
end;
procedure TVintList.buildmilestone;
var
  i:integer;
  tlen:int64;
begin
  if ffilled < Miledistance then exit;

  fmssize:=milestoneneeded;
  freemem(fmilestones);
  fmilestones:=allocmem(fmssize * sizeof(TMilestone));

  tlen:=0;
  nMilestone:=0;
  gobegin;
  if count>0 then for i:=0 to count-1 do begin
    next;
    tlen:=fptr-fdata;
    if(tlen div Miledistance > nMilestone) then begin
      addMileStone(curvalue,curcount);
    end;
  end;

end;
procedure TVintList.addMileStone(v,c:int64);
var
  ms:Pmilestone;
begin
  ms:=fmilestones;
  inc(ms,nmilestone);
  if nmilestone>fmssize then begin
    dec( nmilestone);inc(nmilestone);
    raise exception.create('error adding milestone');
    exit;
  end;
  ms.value := v;
  ms.Count := c;
  inc(nMileStone);
end;

function TVintList_wo.adddelta(const delta:int64):boolean;
var
  d:int64;
  needed:cardinal;

begin
  needed:=0;

  d:=delta;
  repeat
    d := d shr 7;
    inc(needed);
  until d=0;

  d:=delta;
  if ffilled+needed >= fsize then grow;
  (fdata+ffilled)^:=char(MASK_LEADBYTE or (d and $7F));
  inc(ffilled);
  d := d shr 7;

  while d>0 do begin
    (fdata+ffilled)^:=char(MASK_FOLLOWBYTE or (d and $7F));
    inc(ffilled);
    d := d shr 7;
  end;

//  inc(ftotallength,needed);
  inc(lastAdded,delta);
  inc(fcount);

end;
{
//stupid version of get
  if i<0 then begin  //this is for stringbuilder, -1 always return 0 for first string
    result:=0 ; exit;
  end;
  result:=first;
  while i>0 do begin
    next;
    dec(i);
  end;
  result:=curvalue;
}



// nmilestone : vint
// milestone_values: array[0..nmilestone-1] of vint
// milestone_counts: array[0..nmilestone-1] of vint
// values: array of vint ( size - size_of_milestones_and_nmilestone )
{
function TVintList_wo.writetostream(writeit:boolean=true):int64;
var
  i:integer;
  w,lastcount,value,lastvalue:int64;
  ms:Pmilestone;
  m:TMemorystream;
begin
  //write milestone to memory then write to disk

  m:=TMemorystream.create;
// @TODO Alignment

  if (writeit) then begin
    m.Write(nmilestone, sizeof(nmilestone) );
    m.Write(fmilestones^,nmilestone*sizeof(TMilestone));
  end;
  result:=sizeof(nmilestone)+nmilestone*sizeof(TMilestone);

  //the body of packedint
  if writeit and (m.size<>result) then begin
    raise exception.create('error writing milestone');
  end;

  if writeit then begin
    m.Position:=0;

    //256 make sure enough room for writing block header
    if target is TKsanaFileStream then
      TKsanaFileStream(target).expandvolumn(m.size+ffilled+ROOMFORBLOCKHEADER );
    target.copyfrom(m,m.size);
    target.write(fdata^,ffilled);
  end;
  m.free;

  inc(result,ffilled);

  if result<1 then begin
    result:=0;
    raise exception.create('error postlist length');
  end;
end;
}
procedure TVintList.calentries;
var
  ms:PMileStone;
begin
  fcount:=0;
  if nMilestone>0 then begin
    ms:=fmilestones;
    inc(ms,nMilestone-1);
    goMilestone( ms );
  end else begin
    goBegin;
  end;
  fcount:=curcount;
  repeat
    if next <0 then break;
    inc(fcount);
  until false;
  gobegin;
end;

//compress milestone into memory stream  
function TVintList.packmilestone(ms_offset,ms_count:Tstream):Boolean;
var
  i:integer;
  w,value,lastcount,lastvalue:int64;
  ms:Pmilestone;
begin
  result:=false;
  if nmilestone=0 then exit;

  lastvalue:=0;
  ms:=fmilestones;

  //write values array
  for i:=0 to nmilestone-1 do begin
    value :=ms^.value - lastvalue;
    w:=writevint(ms_offset,value);
    lastvalue:=ms^.value;
    inc(ms);
  end;

  lastcount:=0;
  ms:=fmilestones;
  //write counts array
  for i:=0 to nmilestone-1 do begin
    value :=ms^.count - lastcount;
    w:=writevint(ms_count,value);
    lastcount:=ms^.count;
    inc(ms);
  end;

  result:=true;
end;

{
procedure TVintList.readmilestone_map;
var
  mptr:pointer;
  msmap,pmsmap:pchar;
  ms:Pmilestone;
  value,lastcount,lastvalue:int64;
  adv,i:integer;

begin
  reallocmem(fmilestones,sizeof(Tmilestone)*nmilestone);
  ms:=fmilestones;
  lastvalue:=0;

  target.Read(ms^,nmilestone*sizeof(TMilestone));
end;
}
{
procedure TVintList.readmilestone;
var
  ms:Pmilestone;
  value,lastcount,lastvalue:int64;
  i:integer;
begin
  reallocmem(fmilestones,sizeof(Tmilestone)*nmilestone);
  ms:=fmilestones;
  target.read(ms^,nmilestone*sizeof(TMilestone));
end;
 }
{
function TVintList.readfromstream(size:int64=0):int64;
var
  sz,mappos,oldpos:int64;
  mapdiff,i:integer;
begin
  oldpos:=target.Position;
  target.read(nmilestone,sizeof(nmilestone));

  if nmilestone>0 then begin
    if filemap<>0 then readmilestone_map else readmilestone;
  end;

  //size of the data on disk

  sz:=(target.position-oldpos);
  if sz>size then begin
    raise exception.create('error vint size '+inttostr(size)+'<'+inttostr(sz));
    exit;
  end;
  ffilled:=size-sz ;

  //use memory map only for > 64K

  if (filemap<>0) and (ffilled>minmapsize) then begin
    fsize:=ffilled;
    freemem(fdata);
    usefilemap:=true;
    //+4 , make sure the dereference of integer is always success, 2007/8/12
    fdata:=mapview(target.position,ffilled+4,mapptr);
    
  end else begin
    if ffilled>fsize then begin
      reallocmem(fdata,ffilled);
    end;
    target.Read(fdata^,ffilled);
  end;


  calentries;
  ffromdisk:=true;
end;
}

function TVintList.MileStoneCount(ms:PMileStone):integer;
begin
  result:=0;
  if ms=@FirstMileStone then exit;
  result:=(integer(ms)-integer(fmilestones)) div sizeof(TMileStone)+1;
end;

procedure TVintList.goMileStone(ms:PMileStone);

begin
    if ms=nil then exit;
    curcount:=ms.Count ;
    curvalue:=ms.value ;

    fptr:=fdata;

    inc(fptr,MileStoneCount(ms) * MileDistance);
    if fptr>fdata+ffilled then begin
      raise exception.create('pl data error');
      exit;
    end;
    if fptr=fdata+ffilled then exit;
    while (integer(fptr^) and MASK_FOLLOWBYTE)=MASK_FOLLOWBYTE do begin
      if fptr>=fdata+ffilled then break;
      inc(fptr);
    end;
end;
procedure TVintList.clear;
begin
  inherited;
  lastadded:=0;
  fptr:=fdata;
  curvalue:=0;
  curcount:=0;
  nmilestone:=0;
end;

function TVintList.FindValue(const value:int64; var Index: Integer): Boolean;
var
  L, H, I, C: int64;
  pms:PMileStone;
begin
  Result := False;
  L := 0;
  h:=nMileStone -1;

  while L <= H do
  begin
    I := (L + H) shr 1;
    pms:=fmilestones;
    inc(pms,i);
    C :=  pms.value - value ;
    if C < 0 then L := I + 1 else
    begin
      H := I - 1;
      if C = 0 then
      begin
        Result := True;
        L := I;
      end;
    end;
  end;
  Index := L;
end;


function TVintList.FindCount(const count:cardinal; var Index: Integer): Boolean;
var
  L, H, I, C: Integer;
  pms:PMileStone;
begin
  Result := False;
  L := 0;
  h:=nMileStone -1;

  while L <= H do
  begin
    I := (L + H) shr 1;
    pms:=fmilestones;
    inc(pms,i);
    C :=  pms.count - count ;
    if C < 0 then L := I + 1 else
    begin
      H := I - 1;
      if C = 0 then
      begin
        Result := True;
        L := I;
      end;
    end;
  end;
  Index := L;
end;

function TVintList.MilestonebyCount(count:cardinal):PMileStone;
var
  idx:integer;
begin
  result:=@FirstMileStone;
  if nmilestone=0 then exit;
  FindCount(Count,idx);
  if idx>0 then begin
    result:=fmilestones;
    inc(result,idx-1);
  end;
end;

function TVintList.Milestonebyoffset(offset:int64):PMileStone;
var
  idx:integer;
begin
  result:=@FirstMileStone;
  FindValue(offset,idx);
  if idx>0 then begin
    result:=fmilestones;
    inc(result,idx-1);
  end;
end;

function TVintList.Get_ms(i:Integer):int64;
begin

  if (curcount>i) then begin
    goMileStone(milestonebyCount(i));
  end;

  calledNext:=1;
  while curcount<fcount do begin
    if calledNext=MileDistanceMask then begin
      goMileStone(milestonebyCount(i));     // not found in this milestone
    end;

    if next=-1 then break;
    if curcount>i then break;
    inc(calledNext);
  end;

  if curcount<i then curvalue:=-1;
  result:=curvalue;
end;

function TVintList.get(i:integer):int64;
begin
  if fptr=nil then fptr:=fdata;
  if (i<0)  then begin
    result:=0;
  end else if i=0 then begin
    result:=first;
  end else if curcount-1=i then begin
    result:=curvalue;
  end else if curcount=i then begin
    result:=next;  //fast
  end else begin
    result:=get_ms(i);
  end;
end;

function TVintList.next:int64;
var
  v,delta:int64;
  //shift:integer;
  shift:int64;

begin
  delta:=0; shift:=0;

  if fptr>=fdata+ffilled then begin
    atend:=true;
    result:=-1;
    exit;
  end;
  atend:=false;
  shift:=1;
  repeat

  //move atend check out of repeat loop //2005.2.19 , subtle bug...
      v:=( integer(fptr^) and $7f) * shift;

    inc(delta,v);
    shift:=shift*128;
    inc(fptr);

  until (fptr>=fdata+ffilled) or (integer(fptr^) and MASK_FOLLOWBYTE=0) ;

  inc(curcount);
  inc(curvalue,delta);
  result:=curvalue;
end;

function TVintList.prev: int64;
begin
  if curcount>1 then items[curcount-2];
end;
{
function TVintList.prev: int64;
var
  v,delta:int64;
  shift:int64;
begin
  delta:=0; shift:=0;

  if fptr<=fdata then begin
    result:=-1;
    exit;
  end;

  shift:=1;
  repeat
    dec(fptr);
    v:=( integer(fptr^) and $7f);
    delta:=delta*shift +v;
    shift:=shift*128;
  until (integer(fptr^) and MASK_FOLLOWBYTE=0) or (fptr<fdata)  ;

  dec(curcount);
  dec(curvalue,delta);
  result:=curvalue;
end;
}

procedure TVintList.gobegin;
begin
  curcount:=0;
  curvalue:=0;
  fptr:=fdata;
  atend:=false;
end;
function TVintList.justbigger;
var
  calledNext:cardinal;
  ms:Pmilestone;
begin
  if atend then gobegin;

  if (curvalue>offset) then begin
    goMileStone(milestonebyOffset(offset));
  end;

  calledNext:=1;
  if curvalue>offset then begin
    result:=curvalue;
    exit;
  end;

  if curcount=fcount then begin
    atend:=true;
    exit;
  end;
  while curcount<fcount do begin
    if ((calledNext and MileDistanceMask)=0) then begin
      callednext:=0;                            // ;; endless loop yinshun: x
      ms:=milestonebyOffset(offset);
      if ms.value > curvalue then begin
         goMileStone(ms);     // prevent backward jump  2006.4.1

        repeat //20051227 next to target offset right after milestone jump.
          next;
          if curvalue>offset then begin
            result:=curvalue;
            exit;
          end;
        until curcount>=fcount;

      end;
    end;

    next;
    if curvalue>offset then break; // switch with next; prevent endless loop 2005.3.20

    inc(calledNext);
  end;

  result:=curvalue;
end;

function TVintList.first:int64;
begin
  gobegin;
  result:=next;
end;

constructor TVintList.create;
begin
  curvalue:=0;
  exclude:=false;
  temporary:=false;
  wildcard:=0;
  atend:=false;
  fmssize:=defaultmilestone;
  fmilestones:=allocmem(fmssize*sizeof(TMilestone));


  inherited create;//(f);
end;

{
   delete item i
   value=v
   patch with zero

   minimum delta for a real item is 1 (diff+1)

   decreasing all following milestone.count-1, following milestone.value-v

}

{*------------------------------------------------------------------------------
  create from a flushed vints
  merge tempblock and recreate milestone

  @param pl   ParameterDescription
  @return ResultDescription
------------------------------------------------------------------------------*}
procedure TVintList.fromflushed(const pl: TVintList_wo);
var
  data,flusheddata:pchar;
  size,flushedsize:cardinal;
begin
  if not ( assigned(pl) and (pl.flushcount>0)) then begin
    raise exception.create('no pl or no flush');
    exit;
  end;

  data:=pl.fdata;
  size := pl.ffilled;
  mergetempblock(pl.flushptr,data,size,flusheddata,flushedsize);

  freemem(fdata);
  fdata:=flusheddata;
  ffilled:=flushedsize;
  fsize:=flushedsize;

  fcount:=pl.count;
  buildmilestone;
end;

procedure TVintList.copyfrom(const v: TVintList_wo);
begin
  if v.fflushptr <>FLUSH_TERMINATOR then begin
    fromflushed(v);
    exit;
  end;

  freemem(fdata);
  fsize:=v.ffilled;
  ffilled:=fsize;
  getmem(fdata,fsize);
  move(v.fdata^,fdata^,fsize);

  fcount:=v.count;
  buildmilestone;
end;


{
procedure TVintList.fromflushed(const vints:TVints);
var
  rm,flushed:TRomBlock;
begin
  if not ( assigned(vints) and vints.hasflush) then begin
    raise exception.create('no pl or no flush');
    exit;
  end;

  rm.data:=vints.fdata;
  rm.size := vints.ffilled;
  mergetempblock(vints.flushptr,rm,flushed);

  freemem(fdata);
  fdata:=flushed.data;
  ffilled:=flushed.size;
  fsize:=flushed.size;

  fcount:=vints.count;
  buildmilestone;
end;
}
{
procedure TVintList.copyfrom(const vints: TVints);
begin
  if vints.fflushptr <>FLUSH_TERMINATOR then begin
    fromflushed(vints);
    exit;
  end;

  freemem(fdata);
  fsize:=vints.ffilled;
  ffilled:=fsize;
  getmem(fdata,fsize);
  move(vints.fdata^,fdata^,fsize);

  fcount:=vints.count;
  buildmilestone;
end;
}
constructor TVintList_wo.createfromints(ints: PINTEGER; count: integer);
var
  i:integer;
  pi:pinteger;
begin
  create;
  pi:=ints;
  for i:=0 to count-1 do begin
    add( pi^);
    inc(pi);
  end;
end;

function TVintList.justsmaller(offset: int64): int64;
begin
  justbigger(offset);
  if curvalue>offset then prev;
end;

destructor TVintList.destroy;
begin

  inherited;
end;

procedure TVintList.readfromstream(inverted, ms_offset, ms_count: TStream);
begin
  freemem(fdata);
  fsize:=inverted.size;
  ffilled:=fsize;

  getmem(fdata,fsize);
  inverted.Position:=0;
  inverted.read(fdata^,fsize);
//  buildmilestone;
  unpackmilestone(ms_offset,ms_count);

  //calentries is must faster after milestone is contructed
  calentries;   // save entries in database to save time consumed by big postings
end;

procedure TVintList.unpackmilestone(ms_offset, ms_count: Tstream);
var
  ms:Pmilestone;
  value,lastcount,lastvalue:int64;
  i:integer;
begin
  nmilestone:=0;
  //get nmilestone
  ms_count.Position:=0;
  while ms_count.position<ms_count.Size do begin
    inc(nmilestone);
    readvint(ms_count);
  end;

  reallocmem(fmilestones,sizeof(Tmilestone)*nmilestone);

  ms:=fmilestones;
  lastvalue:=0;
  ms_offset.position:=0;
  for i:=0 to nmilestone-1 do begin
    value:=readvint(ms_offset);
    ms.value:=value+lastvalue;
    lastvalue:=ms.value;
    inc(ms);
  end;

  lastcount:=0;
  ms:=fmilestones;
  ms_count.position:=0;
  for i:=0 to nmilestone-1 do begin
    value:=readvint(ms_count);
    ms.count:=value+lastcount;
    lastcount:=ms.count;
    inc(ms);
  end;
end;


initialization
  firstmilestone.value :=0;
  firstmilestone.count :=0;
end.
